import { drawtext, cloneDeep, Bus, guid } from "@/utils/utils.js";
import { DOWN, UP, MOVE } from "@/utils/event.js";
import { isPointInPath } from "@/utils/isPointInPath.js";
import * as math from "mathjs";

export class Canvas {
  constructor(el, { color = "#000", lineWidth = 1, lineCap = "butt" } = {}) {
    this.objects = []; // 缓存节点数据
    this.status = "up";
    this.point = {};
    this.color = color;
    this.lineWidth = lineWidth;
    this.lineCap = lineCap;
    this.bus = new Bus();
    this.canvas = document.querySelector(el);
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    this.ctx = this.canvas.getContext("2d");
    this.initEvent();
  }
  initEvent() {
    this.canvas.addEventListener(DOWN, this.event(DOWN));
    this.canvas.addEventListener(UP, this.event(UP));
    this.canvas.addEventListener(MOVE, this.event(MOVE));
    window.onresize = () => {
      this.canvas.width = window.innerWidth;
      this.canvas.height = window.innerHeight;
      this.render();
    };
  }
  event(name) {
    return (e) => {
      if (name === DOWN) {
        this.status = "down";
        if (!this.point.x) {
          this.point = {
            x: e.offsetX,
            y: e.offsetY,
          };
        }
      }
      if (name === UP) {
        this.status = "up";
        this.point = {};
      }
      if (name == UP) return;
      const items = cloneDeep(this.objects).reverse();
      const { x = 0, y = 0 } = this.point;
      for (let i of items) {
        const current = this.find(i.id);
        if (current.click && name === DOWN && isPointInPath(e, current)) {
          i.click({
            id: i.id,
            x: e.offsetX,
            y: e.offsetY,
            ctx: this.ctx,
            status: this.status,
            objects: this.objects,
          });
        }
        if (
          current.drag &&
          this.status === "down" &&
          isPointInPath(e, current)
        ) {
          this.clear();
          if (current.type === "polygon") {
            const moveX = e.offsetX - x
            const moveY = e.offsetY - y
            const moves = math.matrix([
              [1, 0, 0],
              [0, 1, 0],
              [moveX, moveY, 1],
            ])
            current.props.path = current.props.path.map((j) => {
              return math.multiply([...j, 1],moves)._data
            });
            // current.props.path = current.props.path.map((j) => [
            //   j[0] + e.offsetX - x,
            //   j[1] + e.offsetY - y,
            // ]);
          } else {
            current.props.x = e.offsetX - current.props.w / 2;
            current.props.y = e.offsetY - current.props.h / 2;
          }
          this.up(current.id);
          this.render();
          return;
        }
      }
    };
  }
  add(data) {
    if (!data) return;
    const datas = !Array.isArray(data) ? [data] : data;
    datas.forEach((i) => {
      if(!i.id) {
        i.id = guid();
      }
      this.collections({
        id: i.id,
        type: i.type,
        props: i.props,
        drag: Boolean(i.drag),
        click: i.click,
      });
    });
    this.render();
    return this;
  }
  group(data) {
    if (!data) return;
    let x,
      y,
      w,
      h = 0;
  }
  render() {
    for (let i of this.objects) {
      if (!i.type) {
        console.error("未传入绘画类型type字段");
        return;
      }
      if (i.type === "group") {
        i.children.forEach((j) => {
          this[j.type](j.props);
        });
        i.props.fillStyle = "transparent";
        this.rect(i.props);
      } else {
        this[i.type](i.props);
      }
    }
    return this;
  }
  /**
   * 矩形框
   * @param {} param0
   * @returns
   */
  rect({ x, y, w, h, fillStyle }) {
    this.ctx.save();
    this.ctx.fillStyle = fillStyle ?? this.color;
    this.ctx.fillRect(x, y, w, h);
    this.ctx.restore();
    return this;
  }
  /**
   * 线框
   * @param {lineWidth} 线宽
   * @param {lineCap} 闭合类型 butt(直角)round(圆角)square(外直角)
   * @returns
   */
  stroke({ x, y, w, h, strokeStyle, lineWidth, lineCap }) {
    this.ctx.save();
    this.ctx.strokeStyle = strokeStyle ?? this.color;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.lineCap = lineCap ?? this.lineCap;
    this.ctx.strokeRect(x, y, w, h);
    this.ctx.restore();
    return this;
  }
  /**
   * 圆角矩形线框
   * @param {fill} 圆角矩形或框
   * @param {clip} 圆是否裁剪
   * @returns
   */
  roundrect({
    x,
    y,
    w,
    h,
    r,
    fillStyle,
    strokeStyle,
    lineWidth = 1,
    clip = false,
  }) {
    if (w < 2 * r) {
      r = w / 2;
    }
    if (h < 2 * r) {
      r = h / 2;
    }
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.fillStyle = fillStyle;
    this.ctx.strokeStyle = strokeStyle;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.arc(x + r, y + r, r, this.getAngle(180), this.getAngle(270));
    this.ctx.moveTo(x + r, y);
    this.ctx.lineTo(x + w - r, y);
    this.ctx.arc(x + w - r, y + r, r, this.getAngle(270), this.getAngle(360));
    this.ctx.lineTo(x + w, y + h - r);
    this.ctx.arc(x + w - r, y + h - r, r, 0, this.getAngle(90));
    this.ctx.lineTo(x + r, y + h);
    this.ctx.arc(x + r, y + h - r, r, this.getAngle(90), this.getAngle(180));
    this.ctx.lineTo(x, y + r);
    if (fillStyle) this.ctx.fill();
    if (strokeStyle) this.ctx.stroke();
    if (clip) {
      this.ctx.clip();
    }
    this.ctx.closePath();
    this.ctx.restore();
    return this;
  }
  /**
   * 直线段
   * @param {path} 路径
   * @param {lineWidth} 线宽
   * @param {lineJoin} 设置末端为 round(圆角) bevel(平角)
   * @param {lineCap} 设置末端为 round(圆角) square(平角)
   * @returns
   */
  line({
    path = [],
    strokeStyle,
    lineWidth,
    lineCap = "round",
    lineJoin = "round",
  }) {
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.strokeStyle = strokeStyle ?? this.color;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.lineJoin = lineJoin;
    this.ctx.lineCap = lineCap ?? this.lineCap;
    const [[x, y] = []] = path;
    this.ctx.moveTo(x, y);
    path.forEach((i, k) => {
      if (path[k + 1]) {
        this.ctx.lineTo(path[k + 1][0], path[k + 1][1]);
      }
    });
    if (strokeStyle) this.ctx.stroke();
    this.ctx.restore();
    return this;
  }
  /**
   * 多边形
   * @param {path} 路径
   * @param {lineWidth} 线宽
   * @param {lineJoin} 设置末端为 round(圆角) bevel(平角)
   * @param {lineCap} 设置末端为 round(圆角) square(平角)
   * @returns
   */
  polygon({
    path = [],
    fillStyle,
    strokeStyle,
    lineWidth,
    close,
    lineCap = "round",
    lineJoin = "round",
  }) {
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.fillStyle = fillStyle ?? this.color;
    this.ctx.strokeStyle = strokeStyle ?? this.color;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.lineJoin = lineJoin;
    this.ctx.lineCap = lineCap ?? this.lineCap;
    const [[x, y] = []] = path;
    this.ctx.moveTo(x, y);
    path.forEach((i, k) => {
      if (path[k + 1]) {
        this.ctx.lineTo(path[k + 1][0], path[k + 1][1]);
      }
    });
    if (Boolean(close)) this.ctx.closePath();
    if (fillStyle) this.ctx.fill();
    if (strokeStyle) this.ctx.stroke();
    this.ctx.restore();
    return this;
  }
  /**
   * 圆弧绘制
   * @param {startAngle} 开始的弧度
   * @param {endAngle} 结束的弧度
   * @param {anticlockwise} true为逆时针，false为顺时针
   * @param {close} 是否封闭
   */
  arc({
    x,
    y,
    r,
    fillStyle,
    strokeStyle,
    startAngle,
    endAngle,
    anticlockwise = false,
    close = false,
    lineWidth,
    lineCap,
  }) {
    this.ctx.save();
    this.ctx.fillStyle = fillStyle;
    this.ctx.strokeStyle = strokeStyle;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.lineCap = lineCap ?? this.lineCap;
    this.ctx.beginPath();
    this.ctx.arc(
      x - r,
      y - r,
      r,
      this.getAngle(startAngle),
      this.getAngle(endAngle),
      anticlockwise
    );
    if (Boolean(close)) {
      this.ctx.closePath();
    }
    if (fillStyle) this.ctx.fill();
    if (strokeStyle) this.ctx.stroke();
    this.ctx.restore();
    return this;
  }
  /**
   * 文本绘制
   * @param {style} param0
   * @returns
   */
  text({
    x,
    y,
    t,
    fillStyle,
    maxWidth,
    style = "normal",
    variant = "small-caps",
    weight = "700",
    size = 14,
    lineHeight = 16,
    family = "Microsoft YaHei",
  }) {
    this.ctx.save();
    this.ctx.fillStyle = fillStyle;
    this.ctx.font = `${style} ${variant} ${weight} ${size}px ${family}`;
    drawtext(t, maxWidth ?? this.canvas.width).forEach((i, k) => {
      this.ctx.fillText(i.content, x, y + k * lineHeight);
    });
    this.ctx.restore();
    return this;
  }
  /**
   * 贝塞尔曲线
   * @param {*} param0
   */
  bezierCurveTo({
    x1,
    y1,
    x2,
    y2,
    anglex1,
    angley1,
    anglex2,
    angley2,
    strokeStyle,
    lineWidth,
  }) {
    this.ctx.save();
    this.ctx.strokeStyle = strokeStyle ?? this.color;
    this.ctx.lineWidth = lineWidth ?? this.lineWidth;
    this.ctx.beginPath();
    this.ctx.moveTo(x1, y1);
    this.ctx.bezierCurveTo(anglex1, angley1, anglex2, angley2, x2, y2);
    this.ctx.stroke();
    this.ctx.restore();
    return this;
  }
  /**
   * 图片绘制
   * @param {src} 图片源
   * @param {x} 在画布上放置图像的 x 坐标位置。
   * @param {y} 在画布上放置图像的 y 坐标位置。
   * @returns image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight
   */
  img({
    src = "",
    x,
    y,
    width,
    height,
    sliceX,
    sliceY,
    sliceWidth,
    sliceHeight,
  }) {
    if (!src) return this;
    const img = new Image();
    img.src = src;
    img.onload = () => {
      if (sliceX && sliceY && sliceWidth && sliceHeight) {
        this.ctx.drawImage(
          img,
          x,
          y,
          sliceX,
          sliceY,
          sliceWidth,
          sliceHeight,
          width,
          height
        );
      } else if (width && height) {
        this.ctx.drawImage(img, x, y, width, height);
      } else {
        this.ctx.drawImage(img, x, y);
      }
    };
    return this;
  }
  on(name, callback) {
    this.bus.on(name, callback);
    return this;
  }
  emit(name, params) {
    this.bus.emit(name, params);
    return this;
  }
  clear({ x = 0, y = 0, w, h } = {}) {
    this.ctx.clearRect(x, y, w ?? this.canvas.width, h ?? this.canvas.height);
    return this;
  }
  download(compress = 1) {
    const dataURL = this.canvas.toDataURL("image/png", compress);
    const a = document.createElement("a");
    const event = new MouseEvent("click");
    a.download = "压缩图片";
    a.href = dataURL;
    a.dispatchEvent(event);
    return this;
  }
  up(id) {
    const index = this.findIndex(id);
    const item = this.objects.splice(index, 1);
    this.objects.push(...item);
    return this;
  }
  down(id) {
    const index = this.findIndex(id);
    const item = this.objects.splice(index, 1);
    this.objects.unshift(...item);
    return this;
  }
  find(id) {
    return this.objects.find((i) => i.id === id);
  }
  findIndex(id) {
    return this.objects.findIndex((i) => i.id === id);
  }
  remove(id) {
    const index = this.findIndex(id);
    this.objects.splice(index, 1);
    return this;
  }
  collections(data) {
    const index = this.findIndex(data.id);
    if (index > -1) {
      this.objects.splice(index, 1, data);
    } else {
      this.objects.push(data);
    }
  }
  /**
   * canvas转换为文件
   * @param {*} filename
   * @returns
   */
  file(filename = "canvas") {
    const dataURL = this.canvas.toDataURL("image/png", 1);
    const arr = dataURL.split(",");
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, { type: mime });
  }
  // 角度转换成弧度
  getAngle(arc) {
    return Math.PI * (arc / 180);
  }
}
